#ifndef TABLAHASH_CPP
#define TABLAHASH_CPP


#include "TablaHash.h"
#include "Tupla.h"
#include "Comparador.h"
#include "ComparadorClave.h"

template <class C,class V>
TablaHash<C,V>::TablaHash( Puntero<Comparador<C>> comparador,Puntero<FuncionHash<C>> funcionHash,int largo):comparadorClave(comparador)
{
	this->largo = this->PrimoMasCercano(largo);
	this->tabla = Array<Puntero<NodoTablaHashAbierto<C,V>>>(this->largo);
	for(int i=0; i<(int)tabla.Largo; i++)
	{
		tabla[i] = NULL;
	}
	this->funcionHash = funcionHash;
	this->cantElementos = 0;
}
	


template <class C,class V>
void TablaHash<C,V>::Insertar(const C x, const V y)
{
	int posAInsertar = this->funcionHash->CodigoDeHash(x) % this->largo;
	if(this->tabla[posAInsertar]==NULL)
	{
		this->tabla[posAInsertar] = new NodoTablaHashAbierto<C,V>();
		this->tabla[posAInsertar]->Clave = x;
		this->tabla[posAInsertar]->Valor = y;
		this->tabla[posAInsertar]->Sig = NULL;
	}else
	{
		Puntero<NodoTablaHashAbierto<C,V>> iter = this->tabla[posAInsertar];
		while(iter!=NULL && this->comparadorClave->SonDistintos(x,iter->Clave))
		{
			iter = iter->Sig;
			this->cantElementos++;
		}
		if(iter!=NULL)
		{
			iter->Valor = y;
		}else
		{
			Puntero<NodoTablaHashAbierto<C,V>> nodo = new NodoTablaHashAbierto<C,V>();
			nodo->Clave = x;
			nodo->Valor = y;
			nodo->Sig = this->tabla[posAInsertar];
			this->tabla[posAInsertar] = nodo;
			this->cantElementos++;
		}
	}
}

template <class C,class V>
const V& TablaHash<C,V>::Recuperar(const C x) const
{
	int pos = this->funcionHash->CodigoDeHash(x) % this->largo;

	Puntero<NodoTablaHashAbierto<C,V>> iter = this->tabla[pos];
	while(iter!=NULL && this->comparadorClave->SonDistintos(x,iter->Clave))
	{
		iter = iter->Sig;
	}
	return iter->Valor;
}

template <class C,class V>
bool TablaHash<C,V>::EstaDefinido(const C x) const
{
	int pos = this->funcionHash->CodigoDeHash(x) % this->largo;

	Puntero<NodoTablaHashAbierto<C,V>> iter = this->tabla[pos];
	while(iter!=NULL && this->comparadorClave->SonDistintos(x,iter->Clave))
	{
		iter = iter->Sig;
	}

	return iter!=NULL;
}

template <class C,class V>
bool TablaHash<C,V>::Borrar(const C& c)
{
	int pos = this->funcionHash->CodigoDeHash(c) % this->largo;

	if(this->tabla[pos]!=NULL)
	{
		Puntero<NodoTablaHashAbierto<C,V>> anterior = this->tabla[pos];

		if(comparadorClave->SonIguales(this->tabla[pos]->Clave,c))
		{//si es el primer elemento el que quiero borrar
			Puntero<NodoTablaHashAbierto<C,V>> aux = this->tabla[pos];
			this->tabla[pos] = this->tabla[pos]->Sig;
			aux->Sig = NULL;
			this->cantElementos--;
			return true;
		}else
		{//mas de dos elementos
			while(anterior->Sig->Sig!=NULL && comparadorClave->SonDistintos(anterior->Sig->Clave,c))
			{
				anterior = anterior->Sig;
			}
			if(comparadorClave->SonIguales(anterior->Sig->Clave,c))
			{
				Puntero<NodoTablaHashAbierto<C,V>> aux = anterior->Sig;
				anterior->Sig = anterior->Sig->Sig;
				aux->Sig = NULL;
				this->cantElementos--;
				return true;
			}
		}
	}
	return false;
}

template <class C,class V>
void TablaHash<C,V>::BorrarTodos()
{
	Puntero<NodoTablaHashAbierto<C,V>> aux = NULL;
	for(int i=0;i<this->largo;i++)
	{
		while(this->tabla[i]!=NULL)
		{
			aux = this->tabla[i];
			this->tabla[i] = this->tabla[i]->Sig;
			aux->Sig = NULL;
		}
	}
}

template <class C,class V>
bool TablaHash<C,V>::EstaVacia() const
{
	return this->cantElementos == 0;
}

template <class C,class V>
bool TablaHash<C,V>::EstaLlena() const
{
	return false;
}

template <class C,class V>
Iterador<C> TablaHash<C,V>::Claves() const
{
	Array<C> claves = Array<C>(this->cantElementos);
	Puntero<NodoTablaHashAbierto<C,V>> aux = NULL;
	int cont = 0;

	for(int i=0; i<this->largo;i++)
	{	
		aux = this->tabla[i];
		while(aux!=NULL)
		{
			claves[cont] = aux->Clave;
			aux = aux->Sig;
			cont++;
		}

	}
	return claves.ObtenerIterador();
}

template <class C,class V>
Iterador<V> TablaHash<C,V>::Valores() const
{
	Array<V> valoresTabla = Array<V>(this->cantElementos);

	Puntero<NodoTablaHashAbierto<C,V>> aux = NULL;
	int cont = 0;

	for(int i=0; i<this->largo;i++)
	{	
		aux = this->tabla[i];
		while(aux!=NULL)
		{
			valoresTabla[cont] = aux->Valor;
			aux = aux->Sig;
			cont++;
		}

	}
	return valoresTabla.ObtenerIterador();
}

template <class C,class V>
int TablaHash<C,V>::Tamanio() const
{
	return this->largo;
}

template <class C,class V>
bool TablaHash<C,V>::Recuperar(const C x, V &i) const
{
	int pos = this->funcionHash->CodigoDeHash(x) % this->largo;

	Puntero<NodoTablaHashAbierto<C,V>> iter = this->tabla[pos];
	while(iter!=NULL && this->comparadorClave->SonDistintos(x,iter->Clave))
	{
		iter = iter->Sig;
	}

	if(iter!=NULL)
	{
		iter->Valor=i;
	}
	return iter!=NULL;
}



#endif
